package model

import (
	"errors"
	"fmt"
	"hertz/demo/biz/util"

	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

func FindSelectionById(db *gorm.DB, id uint) (*Selection, error) {
	var ret Selection
	ret.ID = id
	if err := db.Preload("Medias").Take(&ret).Error; err != nil {
		return nil, err
	}
	return &ret, nil
}

func UpdateSelectionStatus(db *gorm.DB, id uint, status string, isAdmin bool, authorId uint) error {
	if !IsValidSelectionStatus(status) {
		return fmt.Errorf("invalid status: %s", status)
	}

	if status == SELECTION_CONFIRM {
		err := db.Transaction(func(tx *gorm.DB) error {
			var origin Selection
			origin.ID = id
			if err := tx.Take(&origin).Error; err != nil {
				return err
			}
			sampleID := origin.SampleID
			if origin.Status == SELECTION_CONFIRM && !isAdmin {
				return util.CanRetError(fmt.Errorf("only admin can change confirm state"))
			}

			err := tx.Model(Selection{}).Where("status = ? AND sample_id = ?", SELECTION_CONFIRM, sampleID).Update("status", SELECTION_WORKING).Error
			if err != nil {
				return err
			}
			return tx.Model(&origin).Update("status", SELECTION_CONFIRM).Error
		})
		return err
	} else {
		if !isAdmin {
			// 非管理员只能修改自己的筛选
			var origin Selection
			origin.ID = id
			if err := db.Take(&origin).Error; err != nil {
				return err
			}
			if origin.AuthorID != authorId {
				return fmt.Errorf("normal user can only modify your own selection")
			}
		}
		return db.Model(&Selection{ID: id}).Update("status", status).Error
	}
}

func UpdateSelectionComment(db *gorm.DB, id uint, comment string, isAdmin bool, authorId uint) error {

	err := db.Transaction(func(tx *gorm.DB) error {
		var origin Selection
		origin.ID = id
		if err := tx.Take(&origin).Error; err != nil {
			return err
		}
		if origin.Status == SELECTION_CONFIRM && !isAdmin {
			return util.CanRetError(fmt.Errorf("only admin can change confirm state selection"))
		}
		if !isAdmin && origin.AuthorID != authorId {
			return fmt.Errorf("normal user can only modify your own selection")
		}
		return tx.Model(&origin).Update("comment", comment).Error
	})
	return err

}

func AddSelection(db *gorm.DB, value *Selection) error {
	if value.Status == "" {
		value.Status = SELECTION_WORKING
	}

	if value.SampleID == "" {
		return fmt.Errorf("missing sample_id")
	}

	if value.Medias != nil {
		for i := range value.Medias {
			value.Medias[i].SampleID = value.SampleID
		}
	}

	if value.Status == SELECTION_CONFIRM {
		return db.Transaction(func(tx *gorm.DB) error {
			var maybe Selection
			err := tx.Where("sample_id = ? AND status = ?", value.SampleID, SELECTION_CONFIRM).Take(&maybe).Error
			if err == nil {
				return fmt.Errorf("can't create second confirmed selection")
			}
			if !errors.Is(err, gorm.ErrRecordNotFound) {
				return err
			} else {
				return tx.Create(value).Error
			}
		})

	} else {
		return db.Create(value).Error
	}
}

func UpdateSelection(db *gorm.DB, value *Selection, isAdmin bool, authorId uint) error {
	// 不能更新状态
	value.Status = ""

	// 不能更新label
	for i := range value.Medias {
		media := value.Medias[i]
		media.Labels = nil
	}

	remainMediaIds := []uint{}
	for i := range value.Medias {
		value.Medias[i].SampleID = value.SampleID
		remainMediaIds = append(remainMediaIds, value.Medias[i].ID)
	}

	session := db.Session(&gorm.Session{FullSaveAssociations: true})

	return session.Transaction(func(tx *gorm.DB) error {
		var origin Selection
		origin.ID = value.ID
		if err := tx.Take(&origin).Error; err != nil {
			return err
		}

		if err := tx.Not(remainMediaIds).Where("selection_id = ?", value.ID).Delete(&Media{}).Error; err != nil {
			return err
		}

		if origin.Status == SELECTION_CONFIRM {
			return util.CanRetError(fmt.Errorf("can't update confirmed selection"))
		}
		if !isAdmin && origin.AuthorID != authorId {
			return fmt.Errorf("normal user can only modify your own selection")
		}

		if err := tx.Updates(value).Error; err != nil {
			return err
		}

		return tx.Take(value).Error
	})
}

func DeleteSelection(db *gorm.DB, id uint, isAdmin bool, authorId uint) error {
	var origin Selection
	origin.ID = id
	if err := db.Take(&origin).Error; err != nil {
		return err
	}
	if !isAdmin && origin.AuthorID != authorId {
		return fmt.Errorf("normal user can only modify your own selection")
	}
	return db.Select(clause.Associations).Delete(&Selection{ID: id}).Error
}

func ListSelection(db *gorm.DB, sampleID string, status string, author *uint64) ([]Selection, error) {
	var ret []Selection

	builder := db.Where("sample_id = ?", sampleID)
	if status != "" {
		builder = builder.Where("status = ?", status)
	}
	if author != nil {
		builder = builder.Where("author_id = ?", *author)
	}

	if err := builder.Find(&ret).Error; err != nil {
		return nil, err
	}

	return ret, nil
}
